home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ohlutil.zip / CREATE.C < prev    next >
C/C++ Source or Header  |  1990-05-31  |  14KB  |  516 lines

  1. /* create -- make new files
  2.    Copyright (C) 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Usage: create [-dfnpqtP] [-m mode] [-x prefix] [+directory] [+fifo] [+pipe]
  19.           [+mode mode] [+no-create] [+path] [+quiet] [+silent] [+temporary]
  20.           [+prefix prefix] [+portability] file...
  21.  
  22.    Construct a valid pathname including each FILE name, make sure that
  23.    file does not already exist, and create an empty file with that name.
  24.    Validation consists of making sure that all directories in the
  25.    constructed pathname exist and have x permission and whether
  26.    strlen (constructed_pathname) <= PATH_MAX
  27.    && strlen (each_directory_in_path) <= NAME_MAX
  28.    Default permissions for created files are rw-rw-rw- - umask.
  29.  
  30.    The created files' names are constructed from:
  31.    [directory/]        from -t (TMPDIR or /tmp)
  32.    [prefix]        from -x
  33.    file            command line args
  34.    [suffix]        from -t
  35.  
  36.    Exit status:
  37.    0            Valid pathnames constructed and (unless -n given)
  38.             files created.
  39.    1            Length of constructed pathname or component
  40.             exceeds limit.
  41.    2            If -P, constructed pathname contains characters
  42.             not in the portable filename character set.
  43.    3            Constructed pathname existed and -p not given;
  44.             or -t given and could not construct nonexisting
  45.             pathname.
  46.    >3            -n not given and couldn't create file because of
  47.             permissions, file system full, etc.
  48.  
  49.    All errors are fatal.
  50.  
  51.    Options:
  52.    -d, +directory    Make a directory instead of a regular file.
  53.             Default permissions are rwxrwxrwx - umask.
  54.  
  55.    -f, +fifo, +pipe    Make a fifo instead of a regular file.
  56.  
  57.    -m, +mode mode    Set the mode of created files to MODE, which
  58.             is symbolic as in chmod and uses the umask as a
  59.             point of departure.
  60.  
  61.    -n, +no-create    Don't check for the file's existence or try to
  62.             create it.
  63.  
  64.    -p, +path        Do not check whether the path prefix or the file
  65.             to be created exist.
  66.             An error occurs if any constructed pathname
  67.             already exists and has a different type than the
  68.             file to create.  Otherwise, if -m is given,
  69.             set the file's permission modes.
  70.             Make any missing parent directories for each argument.
  71.             Parent dirs default to umask modified by `u+wx'.
  72.  
  73.    -q, +quiet, +silent    When given with -t or -x, do not write pathname.
  74.  
  75.    -t, +temporary    Append a SUFFIX of chars in the portable filename
  76.             character set to the FILE name such that the
  77.             resulting pathname is unique and still valid.
  78.             If TMPDIR is set in the environment and neither
  79.             FILE nor PREFIX contains a slash, use the
  80.             value of TMPDIR as the directory pathname;
  81.             otherwise, use /tmp.
  82.             If -m is not given, do go= on the default mode.
  83.             Write the constructed pathname to stdout.
  84.  
  85.    -x, +prefix prefix    Use PREFIX when constructing path names.
  86.             Write the constructed pathname to stdout.
  87.  
  88.    -P, +portability    Instead of performing length checks on the
  89.             underlying filesystem, test the length of the
  90.             pathname and its components against the POSIX.1
  91.             minimum limits for portability, _POSIX_NAME_MAX
  92.             and _POSIX_PATH_MAX in 2.9.2.  Also check that
  93.             the pathname contains no characters not in the
  94.             portable filename character set.
  95.  
  96.    David MacKenzie <djm@ai.mit.edu>  */
  97.  
  98. #include <stdio.h>
  99. #include <sys/types.h>
  100. #include "system.h"
  101. #include "getopt.h"
  102. #include "modechange.h"
  103.  
  104. #ifdef STDC_HEADERS
  105. #include <errno.h>
  106. #include <stdlib.h>
  107. #else
  108. char *getenv ();
  109. char *malloc ();
  110.  
  111. extern int errno;
  112. #endif
  113.  
  114. #ifndef _POSIX_SOURCE
  115. #define mkfifo(path, mode) (mknod ((path), (mode) | S_IFIFO, 0))
  116. #endif
  117.  
  118. int mktemp ();
  119.  
  120. char *construct_path ();
  121. char *xmalloc ();
  122. void ensure_path_exists ();
  123. void error ();
  124. void strip_trailing_slashes ();
  125. void usage ();
  126. void validate_new_path ();
  127.  
  128. /* The name this program was run with. */
  129. char *program_name;
  130.  
  131. struct option longopts[] =
  132. {
  133.   {"directory", 0, NULL, 'd'},
  134.   {"fifo", 0, NULL, 'f'},
  135.   {"pipe", 0, NULL, 'f'},
  136.   {"mode", 1, NULL, 'm'},
  137.   {"no-create", 0, NULL, 'n'},
  138.   {"path", 0, NULL, 'p'},
  139.   {"quiet", 0, NULL, 'q'},
  140.   {"silent", 0, NULL, 'q'},
  141.   {"temporary", 0, NULL, 't'},
  142.   {"prefix", 1, NULL, 'x'},
  143.   {"portability", 0, NULL, 'P'},
  144.   {NULL, 0, NULL, 0}
  145. };
  146.  
  147. void
  148. main (argc, argv)
  149.      int argc;
  150.      char **argv;
  151. {
  152.   unsigned filetype;        /* The type of file to create. */
  153.   int make_missing_parents;    /* If nonzero, ensure that a path exists.  */
  154.   int create_file;        /* If nonzero, try to create the file. */
  155.   int echo_pathnames;        /* If nonzero, write pathnames to stdout. */
  156.   int make_temporary;        /* If nonzero, make file in a temp dir. */
  157.   int check_portability;    /* If nonzero, check POSIX portability. */
  158.   char *tmpdir;            /* Value of TMPDIR variable or "/tmp". */
  159.   int tmpdir_length;        /* Length of `tmpdir' + '/'. */
  160.   unsigned short newmode;
  161.   unsigned short parent_mode;
  162.   struct mode_change *change;
  163.   char *symbolic_mode;
  164.   char *prefix;
  165.   int prefix_length;
  166.   int silent;
  167.   int optc;
  168.   int ind;
  169.  
  170.   program_name = argv[0];
  171.   filetype = S_IFREG;
  172.   make_missing_parents = 0;
  173.   create_file = 1;
  174.   echo_pathnames = 0;
  175.   make_temporary = 0;
  176.   check_portability = 0;
  177.   tmpdir = prefix = "";
  178.   tmpdir_length = 0;
  179.   prefix_length = 0;
  180.   symbolic_mode = NULL;
  181.   silent = 0;
  182.  
  183.   while ((optc = getopt_long (argc, argv, "dfnpqtPm:x:", longopts, &ind))
  184.      != EOF)
  185.     {
  186.       if (optc == 0 && longopts[ind].flag == 0)
  187.     optc = longopts[ind].val;
  188.       switch (optc)
  189.     {
  190.     case 0:            /* Long option. */
  191.       break;
  192.     case 'd':
  193.       filetype = S_IFDIR;
  194.       break;
  195.     case 'f':
  196. #ifdef S_IFIFO
  197.       filetype = S_IFIFO;
  198. #else
  199.       error (4, 0, "fifo files are not supported on this system");
  200. #endif
  201.       break;
  202.     case 'm':
  203.       symbolic_mode = optarg;
  204.       break;
  205.     case 'n':
  206.       create_file = 0;
  207.       break;
  208.     case 'p':
  209.       make_missing_parents = 1;
  210.       break;
  211.     case 'q':
  212.       silent = 1;
  213.       break;
  214.     case 't':
  215.       make_temporary = 1;
  216.       echo_pathnames = 1;
  217.       break;
  218.     case 'x':
  219.       prefix = optarg;
  220.       prefix_length = strlen (prefix);
  221.       echo_pathnames = 1;
  222.       break;
  223.     case 'P':
  224.       check_portability = 1;
  225.       break;
  226.     default:
  227.       usage ();
  228.     }
  229.     }
  230.  
  231.   if (optind == argc)
  232.     usage ();
  233.  
  234.   if (silent)
  235.     echo_pathnames = 0;
  236.   
  237.   newmode = 0777 & ~umask (0);
  238.   parent_mode = newmode | 0300;    /* u+wx */
  239.   if (filetype != S_IFDIR)
  240.     newmode &= ~0111;
  241.   if (symbolic_mode)
  242.     {
  243.       change = mode_compile (symbolic_mode, MODE_MASK_EQUALS | MODE_MASK_PLUS);
  244.       if (change == MODE_INVALID)
  245.     error (4, 0, "invalid mode");
  246.       else if (change == MODE_MEMORY_EXHAUSTED)
  247.     error (4, 0, "virtual memory exhausted");
  248.       newmode = mode_adjust (newmode, change);
  249.     }
  250.   else if (make_temporary)
  251.     newmode &= ~0077;        /* go= */
  252.  
  253.   if (make_temporary || prefix)
  254.     {
  255.       tmpdir = getenv ("TMPDIR");
  256.       if (tmpdir)
  257.     tmpdir_length = strlen (tmpdir) + 1;
  258.     }
  259.  
  260.   for (; optind < argc; ++optind)
  261.     {
  262.       char *file = argv[optind];
  263.  
  264.       strip_trailing_slashes (file);
  265.       if (make_temporary || prefix)
  266.     file = construct_path (file, make_temporary, tmpdir, tmpdir_length,
  267.                    prefix, prefix_length);
  268.  
  269.       if (echo_pathnames)
  270.     puts (file);
  271.  
  272.       if (make_missing_parents)
  273.     ensure_path_exists (file, filetype, newmode, parent_mode);
  274.       else
  275.     validate_new_path (file, check_portability);
  276.       if (create_file)
  277.     {
  278.       switch (filetype)
  279.         {
  280.         case S_IFREG:
  281.           ind = open (file, O_WRONLY | O_CREAT | O_EXCL, newmode);
  282.           if (ind == -1)
  283.         error (4, errno, "%s", file);
  284.           else
  285.         close (ind);
  286.           break;
  287. #ifdef S_IFIFO
  288.         case S_IFIFO:
  289.           if (mkfifo (file, newmode))
  290.         error (4, errno, "%s", file);
  291.           break;
  292. #endif
  293.         case S_IFDIR:
  294.           if (mkdir (file, newmode))
  295.         error (4, errno, "%s", file);
  296.           break;
  297.         }
  298.     }
  299.       if (file != argv[optind])
  300.     free (file);
  301.     }
  302.  
  303.   exit (0);
  304. }
  305.  
  306. /* Return a path with the form:
  307.    [directory/]        TMPDIR or /tmp if MAKE_TEMPORARY, else empty
  308.    [prefix]        PREFIX if set, else empty
  309.    file            BASE
  310.    [suffix]        letter+pid if MAKE_TEMPORARY, else empty */
  311.  
  312. char *
  313. construct_path (base, make_temporary, tmpdir, tmpdir_length,
  314.         prefix, prefix_length)
  315.      char *base;
  316.      int make_temporary;
  317.      char *tmpdir;
  318.      int tmpdir_length;
  319.      char *prefix;
  320.      int prefix_length;
  321. {
  322.   char *path, *p;
  323.   int base_length;
  324.  
  325.   base_length = strlen (base);
  326.   if (make_temporary)
  327.     {
  328.       if (tmpdir == NULL || index (base, '/') || index (prefix, '/'))
  329.     {
  330.       tmpdir = "/tmp";
  331.       tmpdir_length = 5;    /* Count a trailing slash. */
  332.     }
  333.     }
  334.   path = xmalloc (tmpdir_length + prefix_length + base_length
  335.           + (make_temporary ? 6 : 0) + 1);
  336.  
  337.   p = path;
  338.   if (make_temporary)
  339.     {
  340.       strcpy (p, tmpdir);
  341.       p += tmpdir_length - 1;
  342.       *p++ = '/';
  343.     }
  344.   strcpy (p, prefix);
  345.   p += prefix_length;
  346.   strcpy (p, base);
  347.   if (make_temporary)
  348.     {
  349.       p += base_length;
  350.       strcpy (p, "XXXXXX");
  351.       mktemp (path);
  352.     }
  353.   return path;
  354. }
  355.  
  356. /* Make sure file PATH, which has type PATHTYPE,
  357.    and all leading directories exist,
  358.    and give it permission mode MODE.
  359.    If any leading directories are created, give them permission
  360.    mode PARENT_MODE.
  361.    Exit if an error occurs. */
  362.  
  363. void
  364. ensure_path_exists (path, pathtype, mode, parent_mode)
  365.      char *path;
  366.      unsigned pathtype;
  367.      unsigned short mode;
  368.      unsigned short parent_mode;
  369. {
  370.   char *slash;
  371.   struct stat stats;
  372.  
  373.   if (stat (path, &stats))
  374.     {
  375.       slash = path;
  376.       while (slash = index (slash, '/'))
  377.     {
  378.       *slash = 0;
  379.       if (stat (path, &stats))
  380.         {
  381.           if (mkdir (path, parent_mode))
  382.         error (4, errno, "cannot make directory `%s'", path);
  383.         }
  384.       else if ((stats.st_mode & S_IFMT) != S_IFDIR)
  385.         error (4, 0, "`%s' is not a directory", path);
  386.       *slash++ = '/';
  387.     }
  388.     }
  389.   else if ((stats.st_mode & S_IFMT) != pathtype)
  390.     error (4, 0, "`%s' has the wrong type", path);
  391.   else if (chmod (path, mode))
  392.     error (4, errno, "cannot change mode of `%s'", path);
  393. }
  394.  
  395. /* Each element is nonzero if the corresponding ASCII character is
  396.    in the POSIX portable character set, and zero if it is not.
  397.    In addition, the entry for `/' is nonzero to simplify checking. */
  398. char portable_chars[] =
  399. {
  400.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0-15 */
  401.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 16-31 */
  402.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 32-47 */
  403.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 48-63 */
  404.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 64-79 */
  405.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 80-95 */
  406.   0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 96-111 */
  407.   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 112-127 */
  408.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  409.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  410.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  411.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  412.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  413.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  414.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
  415.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  416. };
  417.  
  418. /* Make sure that PATH does not exist, but that all directories along
  419.    the way do exist and have `x' permission, and that
  420.    strlen (PATH) <= PATH_MAX
  421.    && strlen (each-directory-in-PATH) <= NAME_MAX
  422.    If PORTABILITY is nonzero, compare against _POSIX_PATH_MAX and
  423.    _POSIX_NAME_MAX instead, and make sure that PATH contains no
  424.    characters not in the POSIX portable filename character set, which
  425.    consists of A-Z, a-z, 0-9, ., _, -.
  426.    Exit if an error occurs. */
  427.  
  428. void
  429. validate_new_path (path, portability)
  430.      char *path;
  431.      int portability;
  432. {
  433.   char *start, *end;
  434.   int name_max, path_max;
  435.   struct stat stats;
  436.  
  437.   /* Perform the cheaper tests first. */
  438.  
  439.   path_max = portability ? _POSIX_PATH_MAX : PATH_MAX;
  440.   name_max = portability ? _POSIX_NAME_MAX : NAME_MAX;
  441.  
  442.   if (strlen (path) > path_max)
  443.     error (1, 0, "path `%s' exceeds length limit", path);
  444.  
  445.   if (portability)
  446.     {
  447.       for (start = path; *start; ++start)
  448.     if (portable_chars[*start] == 0)
  449.       error (2, 0, "path `%s' contains nonportable character `%c'",
  450.          path, *start);
  451.     }
  452.  
  453.   if (stat (path, &stats) == 0)
  454.     error (3, 0, "`%s' already exists", path);
  455.  
  456.   start = path;
  457.   do
  458.     {
  459.       end = index (start + 1, '/');
  460.       if (end == NULL)
  461.     end = index (start + 1, 0);
  462.       if (end - start - (*start == '/') > name_max)
  463.     error (1, 0, "name `%s' exceeds length limit", path);
  464.       if (*end == '/')
  465.     {
  466.       *end = 0;
  467.       if (stat (path, &stats) == -1)
  468.         error (4, errno, "%s", path);
  469.       else if ((stats.st_mode & S_IFMT) != S_IFDIR)
  470.         error (4, 0, "`%s' is not a directory", path);
  471.       *end = '/';
  472.     }
  473.       start = end;
  474.     }
  475.   while (*start);
  476. }
  477.  
  478. /* Remove trailing slashes from PATH; they cause some system calls to fail. */
  479.  
  480. void
  481. strip_trailing_slashes (path)
  482.      char *path;
  483. {
  484.   int last;
  485.  
  486.   last = strlen (path) - 1;
  487.   while (last > 0 && path[last] == '/')
  488.     path[last--] = '\0';
  489. }
  490.  
  491. /* Allocate N bytes of memory dynamically, with error checking.  */
  492.  
  493. char *
  494. xmalloc (n)
  495.      unsigned n;
  496. {
  497.   char *p;
  498.  
  499.   p = malloc (n);
  500.   if (p == 0)
  501.     error (4, 0, "virtual memory exhausted");
  502.   return p;
  503. }
  504.  
  505. void
  506. usage ()
  507. {
  508.   fprintf (stderr, "\
  509. Usage: %s [-dfnpqtP] [-m mode] [-x prefix] [+directory] [+fifo] [+pipe]\n\
  510.        [+mode mode] [+no-create] [+path] [+quiet] [+silent] [+temporary]\n\
  511.        [+prefix prefix] [+portability] file...\n",
  512.        program_name);
  513.   exit (1);
  514. }
  515.  
  516.